/*
 * Copyright 2019,2021-2023 NXP
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifdef UWBIOT_USE_FTR_FILE
#include "uwb_iot_ftr.h"
#else
#include "uwb_iot_ftr_default.h"
#endif

#include "phUwbTypes.h"

#include "phUwb_BuildConfig.h"
#include "phOsalUwb.h"
#include "phUwbErrorCodes.h"
#include "phNxpLogApis_TmlUwb.h"
#include <uwb_uwbs_tml_interface.h>
#include "UWB_Evt.h"
#include "uwb_logging.h"

#if UWBIOT_TML_SOCKET
#include <windows.h>
#endif

#define PAYLOAD_LEN_MSB  0x02
#define PAYLOAD_LEN_LSB  0x03
#define UCI_HDR_LEN      0x04
#define HBCI_HEADER_SIZE 0x04

#define NORMAL_MODE_LEN_OFFSET          0x03
#define EXTND_LEN_INDICATOR_OFFSET      0x01
#define EXTND_LEN_INDICATOR_OFFSET_MASK 0x80
#define EXTENDED_LENGTH_OFFSET          0x02

#define UCI               0x00
#define GET_BOARD_ID      0x80
#define GET_BOARD_VERSION 0x81
#define HARD_RESET        0x82
#define SET_SPI_RATE      0x83
#define GET_SPI_RATE      0x84
#define SWITCH_PROTOCOL   0x85
#define SWUP              0x05
#define ERROR_MSG         0xFF

#define USB_RESPONSE_HEADER_LEN 0x06
#define USB_WRITE_HEADER_OFFSET 0x06
#define COMMAND                 0x00
#define RESPONSE                0x01

static uint8_t tx_buffer[2060];
static uint8_t rx_buffer[2060];

/** Local functions prototype */
static UWBStatus_t sendCommand(uwb_uwbs_tml_ctx_t *pCtx, uint8_t commandType, uint8_t *buffer, uint32_t length);

#define MAX_RETRY_COUNT 9
#if UWBIOT_TML_SOCKET
#define phOsalUwb_Delay Sleep
#endif

/* Used only for HARD_RESET currently */
static UWBStatus_t sendCommand(uwb_uwbs_tml_ctx_t *pCtx, uint8_t commandType, uint8_t *buffer, uint32_t length)
{
    uwb_bus_status_t bus_status = kUWB_bus_Status_FAILED;
    uint32_t payloadLength      = 0;
    uint8_t max_retry           = 0;

    tx_buffer[0] = commandType;
    tx_buffer[1] = COMMAND;
    tx_buffer[2] = (uint8_t)((length >> (8 * 0)) & 0xFF);
    tx_buffer[3] = (uint8_t)((length >> (8 * 1)) & 0xFF);
    tx_buffer[4] = (uint8_t)((length >> (8 * 2)) & 0xFF);
    tx_buffer[5] = (uint8_t)((length >> (8 * 3)) & 0xFF);

    /* Copy buffer into tx_buffer */
    if (length <= 2054) {
        phOsalUwb_MemCopy(&tx_buffer[6], buffer, length);
    }
    else {
        LOG_E("Very long data");
        return kUWBSTATUS_FAILED;
    }

    LOG_TX("Proprietary TX ", tx_buffer, USB_WRITE_HEADER_OFFSET + length);
    bus_status = uwb_bus_data_tx(&pCtx->busCtx, tx_buffer, USB_WRITE_HEADER_OFFSET + length);

    if (commandType == HARD_RESET) {
        /* Upon HardReset, if we read immediately, S32 always returns with 0xFF, in order to
           give S32 some time for Resetting its state, this delay is added.*/
        phOsalUwb_Delay(50);
    }

    if (bus_status == kUWB_bus_Status_OK) {
    retry:
        phOsalUwb_SetMemory(rx_buffer, 0, sizeof(rx_buffer));
        bus_status = uwb_bus_data_rx(&pCtx->busCtx, rx_buffer, USB_RESPONSE_HEADER_LEN);
        if ((kUWB_bus_Status_OK == bus_status) && (max_retry < MAX_RETRY_COUNT)) {
            max_retry++;
            goto retry;
        }
        else if (kUWB_bus_Status_OK == bus_status) {
            LOG_RX("Proprietary RX ", rx_buffer, USB_RESPONSE_HEADER_LEN);
            if (rx_buffer[0] == HARD_RESET && rx_buffer[1] == RESPONSE) {
                /* hard Reset Command does not have any payload
                hence return immediately */
                return kUWBSTATUS_FAILED;
            }
            /* If the Response is other than Hard Reset, then read payload and ignore it */
            payloadLength = (uint32_t)((rx_buffer[5] << 8u * 3u) | (rx_buffer[4] << 8u * 2u) |
                                       (rx_buffer[3] << 8u * 1u) | (rx_buffer[2] << 8u * 0u));

            if (payloadLength > 0) {
                bus_status = uwb_bus_data_rx(&pCtx->busCtx, rx_buffer, payloadLength);
                LOG_RX("Proprietary RX ", rx_buffer, payloadLength);
            }
        }
    }
    else {
        LOG_E("ERROR! Send Failed");
        return kUWBSTATUS_FAILED;
    }

    return kUWBSTATUS_SUCCESS;
}

UWBStatus_t uwb_uwbs_tml_init(uwb_uwbs_tml_ctx_t *pCtx)
{
    UWBStatus_t status          = kUWBSTATUS_FAILED;
    uwb_bus_status_t bus_status = kUWB_bus_Status_FAILED;
    uint8_t flush_buffer[256]   = {0};

    if (pCtx == NULL) {
        LOG_E("uwbs tml context is NULL");
        status = kUWBSTATUS_INVALID_PARAMETER;
        goto exit;
    }

    phOsalUwb_SetMemory(pCtx, 0, sizeof(uwb_uwbs_tml_ctx_t));

    bus_status = uwb_bus_init(&pCtx->busCtx);
    if (bus_status != kUWB_bus_Status_OK) {
        LOG_E("uwb_uwbs_tml_init : uwb_bus_init failed");
        goto exit;
    }

    bus_status = uwb_bus_data_rx(&pCtx->busCtx, flush_buffer, sizeof(flush_buffer));
    if (bus_status == kUWB_bus_Status_OK) {
        LOG_D("Flushed read buffer()");
    }

    uwb_uwbs_tml_switch_protocol(pCtx, kUWB_UWBS_TML_MODE_UCI);
    sendCommand(pCtx, HARD_RESET, NULL, 0);
    status = kUWBSTATUS_SUCCESS;
exit:
    return status;
}

UWBStatus_t uwb_uwbs_tml_setmode(uwb_uwbs_tml_ctx_t *pCtx, uwb_uwbs_tml_mode_t mode)
{
    if (pCtx == NULL) {
        LOG_E("uwbs tml context is NULL");
        return kUWBSTATUS_INVALID_PARAMETER;
    }
    pCtx->mode = mode;
    return kUWBSTATUS_SUCCESS;
}

UWBStatus_t uwb_uwbs_tml_deinit(uwb_uwbs_tml_ctx_t *pCtx)
{
    uwb_bus_deinit(&pCtx->busCtx);
    return kUWBSTATUS_SUCCESS;
}

UWBStatus_t uwb_uwbs_tml_data_tx(uwb_uwbs_tml_ctx_t *pCtx, uint8_t *pBuf, size_t bufLen)
{
    uwb_bus_status_t bus_status = kUWB_bus_Status_FAILED;
    UWBStatus_t status          = kUWBSTATUS_FAILED;
    pCtx->noOfBytesWritten      = -1;

    if (pCtx == NULL) {
        LOG_E("uwbs tml context is NULL");
        status = kUWBSTATUS_INVALID_PARAMETER;
        goto end;
    }

    if (pBuf == NULL || bufLen == 0) {
        LOG_E("write buffer is Null or bufLen is 0");
        status = kUWBSTATUS_INVALID_PARAMETER;
        goto end;
    }

    if (pCtx->mode == kUWB_UWBS_TML_MODE_UCI) {
        tx_buffer[0] = UCI;
    }
    else if (pCtx->mode == kUWB_UWBS_TML_MODE_SWUP) {
        tx_buffer[0] = SWUP;
    }
    else {
        LOG_E("UWBS TML Mode is not supported for S32 UART");
        goto end;
    }

    tx_buffer[1] = COMMAND;
    tx_buffer[2] = (bufLen >> 8u * 0u) & 0xFFu;
    tx_buffer[3] = (bufLen >> 8u * 1u) & 0xFFu;
    tx_buffer[4] = 0x00;
    tx_buffer[5] = 0x00;
    phOsalUwb_MemCopy(&tx_buffer[USB_WRITE_HEADER_OFFSET], pBuf, (uint32_t)bufLen);

    bus_status = uwb_bus_data_tx(&pCtx->busCtx, tx_buffer, USB_WRITE_HEADER_OFFSET + bufLen);
    if (bus_status == kUWB_bus_Status_OK) {
        status                 = kUWBSTATUS_SUCCESS;
        pCtx->noOfBytesWritten = (int)bufLen;
    }
end:
    return status;
}

UWBStatus_t uwb_uwbs_tml_data_rx(uwb_uwbs_tml_ctx_t *pCtx, uint8_t *pBuf, size_t *pBufLen)
{
    uwb_bus_status_t bus_status = kUWB_bus_Status_FAILED;
    UWBStatus_t status          = kUWBSTATUS_FAILED;
    uint32_t uciPacketLength    = 0;
    uint32_t retryCount         = 5;

    if (pCtx == NULL) {
        LOG_E("uwbs tml context is NULL");
        status = kUWBSTATUS_INVALID_PARAMETER;
        goto end;
    }

    if (pBuf == NULL || pBufLen == NULL) {
        LOG_E("read buffer or bufLen is NULL");
        status = kUWBSTATUS_INVALID_PARAMETER;
        goto end;
    }
    *pBufLen = 0;

    if (pCtx->mode == kUWB_UWBS_TML_MODE_SWUP) {
        retryCount = 9;
    }

retry:
    phOsalUwb_SetMemory(rx_buffer, 0, sizeof(rx_buffer));
    bus_status = uwb_bus_data_rx(&pCtx->busCtx, &rx_buffer[0], USB_RESPONSE_HEADER_LEN);
    if (bus_status == kUWB_bus_Status_OK) {
        uciPacketLength = (uint32_t)((rx_buffer[5] << 8u * 3u) | (rx_buffer[4] << 8u * 2u) | (rx_buffer[3] << 8u * 1u) |
                                     (rx_buffer[2] << 8u * 0u));

        if (uciPacketLength != 0) {
            if (rx_buffer[0] == ERROR_MSG) {
                bus_status = uwb_bus_data_rx(&pCtx->busCtx, &rx_buffer[0], uciPacketLength);
                if (bus_status != kUWB_bus_Status_OK) {
                    goto end;
                }
                if (retryCount-- > 0) {
                    goto end;
                }
                goto retry;
            }
            bus_status = uwb_bus_data_rx(&pCtx->busCtx, pBuf, uciPacketLength);
            if (bus_status == kUWB_bus_Status_OK) {
                status = kUWBSTATUS_SUCCESS;
            }
        }
        *pBufLen = uciPacketLength;
    }
    else {
        *pBufLen = 0;
    }
end:
    return status;
}

UWBStatus_t uwb_uwbs_tml_reset(uwb_uwbs_tml_ctx_t *pCtx)
{
    /* Not used in UART tml */
    return kUWBSTATUS_SUCCESS;
}

void uwb_uwbs_tml_flush_read_buffer(uwb_uwbs_tml_ctx_t *pCtx)
{
    /* Not needed here*/
    return;
}
void uwb_uwbs_tml_switch_protocol(uwb_uwbs_tml_ctx_t *pCtx, uwb_uwbs_tml_mode_t mode)
{
    sendCommand(pCtx, SWITCH_PROTOCOL, (uint8_t *)&mode, 0x01);
}
